package com.kg.rails.ui;

import com.kg.rails.component.editor.io.Codec;
import com.kg.rails.component.editor.model.Cell;
import com.kg.rails.component.editor.model.Geometry;
import com.kg.rails.component.editor.model.ICell;
import com.kg.rails.component.editor.model.IGraphModel;
import com.kg.rails.component.editor.swing.GraphComponent;
import com.kg.rails.component.editor.swing.util.GraphTransferable;
import com.kg.rails.component.editor.util.*;
import com.kg.rails.component.editor.util.EventSource.mxIEventListener;
import com.kg.rails.component.editor.view.CellState;
import com.kg.rails.component.editor.view.Graph;
import com.kg.rails.ui.editor.BasicGraphEditor;
import com.kg.rails.ui.editor.EditorPalette;
import org.w3c.dom.Document;

import javax.swing.*;
import java.awt.*;
import java.net.URL;
import java.text.NumberFormat;
import java.util.Iterator;
import java.util.List;

public class MainFrame extends BasicGraphEditor {
    private static final long serialVersionUID = -4601740824088314699L;

    public static final String NODE = "Стрелка";
    public static final String STATION = "Станция";
    public static final String ELEMENT = "Блок-участок";

    public static URL url = null;
    public static final NumberFormat numberFormat = NumberFormat.getInstance();


    public MainFrame() {
        this("Редактор железнодорожной инфраструктуры [Kg.Rails.Editor]", new CustomGraphComponent(new CustomGraph()));
    }

    public MainFrame(String appTitle, GraphComponent component) {
        super(appTitle, component);
        final Graph graph = graphComponent.getGraph();



        EditorPalette shapesPalette = insertPalette(Resources.get("shapes"));

        // Sets the edge template to be used for creating new edges if an edge
        // is clicked in the shape palette
        shapesPalette.addListener(EditorEvent.SELECT, new mxIEventListener() {
            public void invoke(Object sender, EditorEventObject evt) {
                Object tmp = evt.getProperty("transferable");

                if (tmp instanceof GraphTransferable) {
                    GraphTransferable t = (GraphTransferable) tmp;
                    Object cell = t.getCells()[0];

                    if (graph.getModel().isEdge(cell)) {
                        ((CustomGraph) graph).setEdgeTemplate(cell);
                    }
                }
            }

        });

        shapesPalette
                .addTemplate(
                        STATION,
                        new ImageIcon(
                                MainFrame.class
                                        .getResource("/com/kg/rails/ui/images/rectangle.png")),
                        "group", 160, 120, "");
        shapesPalette
                .addTemplate(
                        ELEMENT,
                        new ImageIcon(
                                MainFrame.class
                                        .getResource("/com/kg/rails/ui/images/rounded.png")),
                        "rounded=1", 90, 90, "");

        shapesPalette
                .addTemplate(
                        NODE,
                        new ImageIcon(
                                MainFrame.class
                                        .getResource("/com/kg/rails/ui/images/ellipse.png")),
                        "shape=ellipse", 50, 50, "");
    }

    public static class CustomGraphComponent extends GraphComponent {
        private static final long serialVersionUID = -6833603133512882012L;

        public CustomGraphComponent(Graph graph) {
            super(graph);

            // Sets switches typically used in an editor
            setGridVisible(true);
            setToolTips(true);
            getConnectionHandler().setCreateTarget(true);

            // Loads the defalt stylesheet from an external file
            Codec codec = new Codec();
            Document doc = EditorUtils.loadDocument(MainFrame.class.getResource(
                    "/com/kg/rails/ui/resources/default-style.xml")
                    .toString());
            codec.decode(doc.getDocumentElement(), graph.getStylesheet());

            // Sets the background to white
            getViewport().setOpaque(true);
            getViewport().setBackground(Color.WHITE);
        }

        /**
         * Overrides drop behaviour to set the cell style if the target
         * is not a valid drop target and the cells are of the same
         * type (eg. both vertices or both edges).
         */
        public Object[] importCells(Object[] cells, double dx, double dy,
                                    Object target, Point location) {
            if (target == null && cells.length == 1 && location != null) {
                target = getCellAt(location.x, location.y);

                if (target instanceof ICell && cells[0] instanceof ICell) {
                    ICell targetCell = (ICell) target;
                    ICell dropCell = (ICell) cells[0];

                    if (!"group".equals(targetCell.getStyle()) && (targetCell.isVertex() == dropCell.isVertex()
                            || targetCell.isEdge() == dropCell.isEdge())) {
                        IGraphModel model = graph.getModel();
                        model.setStyle(target, model.getStyle(cells[0]));
                        graph.setSelectionCell(target);

                        return null;
                    }
                }
            }

            return super.importCells(cells, dx, dy, target, location);
        }

    }

    /**
     * A graph that creates new edges from a given template edge.
     */
    public static class CustomGraph extends Graph {
        /**
         * Holds the edge to be used as a template for inserting new edges.
         */
        protected Object edgeTemplate;

        /**
         * Custom graph that defines the alternate edge style to be used when
         * the middle control point of edges is double clicked (flipped).
         */
        public CustomGraph() {
            setAlternateEdgeStyle("edgeStyle=EdgeStyle.ElbowConnector;elbow=vertical");
        }

        /**
         * Sets the edge template to be used to inserting edges.
         */
        public void setEdgeTemplate(Object template) {
            edgeTemplate = template;
        }

        /**
         * Prints out some useful information about the cell in the tooltip.
         */
        public String getToolTipForCell(Object cell) {
            String tip = "<html>";
            Geometry geo = getModel().getGeometry(cell);
            CellState state = getView().getState(cell);

            if (getModel().isEdge(cell)) {
                tip += "points={";

                if (geo != null) {
                    List<EditorPoint> points = geo.getPoints();

                    if (points != null) {
                        Iterator<EditorPoint> it = points.iterator();

                        while (it.hasNext()) {
                            EditorPoint point = it.next();
                            tip += "[x=" + numberFormat.format(point.getX())
                                    + ",y=" + numberFormat.format(point.getY())
                                    + "],";
                        }

                        tip = tip.substring(0, tip.length() - 1);
                    }
                }

                tip += "}<br>";
                tip += "absPoints={";

                if (state != null) {

                    for (int i = 0; i < state.getAbsolutePointCount(); i++) {
                        EditorPoint point = state.getAbsolutePoint(i);
                        tip += "[x=" + numberFormat.format(point.getX())
                                + ",y=" + numberFormat.format(point.getY())
                                + "],";
                    }

                    tip = tip.substring(0, tip.length() - 1);
                }

                tip += "}";
            } else {
                tip += "geo=[";

                if (geo != null) {
                    tip += "x=" + numberFormat.format(geo.getX()) + ",y="
                            + numberFormat.format(geo.getY()) + ",width="
                            + numberFormat.format(geo.getWidth()) + ",height="
                            + numberFormat.format(geo.getHeight());
                }

                tip += "]<br>";
                tip += "state=[";

                if (state != null) {
                    tip += "x=" + numberFormat.format(state.getX()) + ",y="
                            + numberFormat.format(state.getY()) + ",width="
                            + numberFormat.format(state.getWidth())
                            + ",height="
                            + numberFormat.format(state.getHeight());
                }

                tip += "]";
            }

            EditorPoint trans = getView().getTranslate();

            tip += "<br>scale=" + numberFormat.format(getView().getScale())
                    + ", translate=[x=" + numberFormat.format(trans.getX())
                    + ",y=" + numberFormat.format(trans.getY()) + "]";
            tip += "</html>";

            return tip;
        }

        /**
         * Overrides the method to use the currently selected edge template for
         * new edges.
         */
        public Object createEdge(Object parent, String id, Object value,
                                 Object source, Object target, String style) {
            if (edgeTemplate != null) {
                Cell edge = (Cell) cloneCells(new Object[]{edgeTemplate})[0];
                edge.setId(id);

                return edge;
            }

            return super.createEdge(parent, id, value, source, target, style);
        }
    }
}
